﻿using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Net.Sockets;

namespace Tools.Debugging {
    
    public class StreamMultiplexer : Stream{
        public enum ReadOrWrite { Read, Write, ReadWrite };
        protected Stream masterStream;

        protected LinkedList<TextWriter> atread = new LinkedList<TextWriter>( );
        protected LinkedList<TextWriter> atwrite = new LinkedList<TextWriter>( );
        //protected LinkedList<TextWriter> atreadwrite = new LinkedList<TextWriter>( );
        protected LinkedList<TextWriter> all = new LinkedList<TextWriter>( );
        public void AddStream ( Stream stream ) {
            this.AddWriter( new StreamWriter( stream ) );
        }
        public void AddWriter ( TextWriter stream ) {
            this.AddWriter( stream, ReadOrWrite.ReadWrite );
        }
        public void AddWriter ( TextWriter stream, ReadOrWrite rw ) {
            this.all.AddLast( stream );
            switch ( rw ) {
                case ReadOrWrite.Read:
                    this.atread.AddLast( stream );
                    break;
                case ReadOrWrite.Write:
                    this.atwrite.AddLast( stream );
                    break;
                case ReadOrWrite.ReadWrite:
                    this.atread.AddLast( stream );
                    this.atwrite.AddLast( stream );
                    break;
            }
        }

        public StreamMultiplexer ( Stream masterStream ) {
            this.masterStream = masterStream;
        }

        public override bool CanRead {
            get { return this.masterStream.CanRead; }
        }

        public override bool CanSeek {
            get { return this.masterStream.CanSeek; }
        }

        public override bool CanWrite {
            get { return this.masterStream.CanWrite; }
        }

        public override void Flush ( ) {
            this.masterStream.Flush( );
            foreach ( TextWriter s in this.all ) {
                s.Flush( );
            }
        }

        public override long Length {
            get { return this.masterStream.Length; }
        }

        public override long Position {
            get {
                return this.masterStream.Position;
            }
            set {
                this.masterStream.Position = value;
            }
        }

        public override int Read ( byte[] buffer, int offset, int count ) {

            int read = this.masterStream.Read( buffer, offset, count );
            this.WriteToSlaves( buffer, offset, count, ReadOrWrite.Read );
            return read;
        }

        public override long Seek ( long offset, SeekOrigin origin ) {
            return this.masterStream.Seek( offset, origin );
        }

        public override void SetLength ( long value ) {
            this.masterStream.SetLength( value );
        }

        public override void Write ( byte[] buffer, int offset, int count ) {
            this.masterStream.Write( buffer, offset, count );
            this.WriteToSlaves( buffer, offset, count, ReadOrWrite.Write );

        }

        protected void WriteToSlaves ( byte[] buff, int offset, int count, ReadOrWrite rw ) {

            LinkedList<TextWriter> l = null;
            switch ( rw ) {
                case ReadOrWrite.Read: l = this.atread; break;
                case ReadOrWrite.Write: l = this.atwrite; break;
                //case ReadOrWrite.ReadWrite: l = this.all; break;
            }
            foreach ( TextWriter sw in l ) {
                //StreamWriter sw = new StreamWriter( s );

                switch ( rw ) {
                    case ReadOrWrite.Read:
                        sw.Write( "read at {0}, length {1}\n[", offset, count );
                        break;
                    case ReadOrWrite.Write:

                        sw.Write( "write at {0}, length {1}\n[", offset, count );
                        break;
                }

                char[] b = Encoding.ASCII.GetChars( buff, offset, count );
                sw.Write( b );
                sw.WriteLine( "]\n------------" );
                sw.Flush( );
            }
        }
    }
}
